package com.core.task;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class TaskManager {

	private static final Logger log = LoggerFactory.getLogger(TaskManager.class);

	private final Integer DEFAULT_CORE_SIZE = 5;
	private final Integer DEFAULT_POOL_SIZE = 50;
	private final Integer DEFAULT_QUEUE_SIZE = 200;
	private final Long DEFAULT_KEEP_ALIVE = 5L;
	
	private final ArrayBlockingQueue<Runnable> queue;
	
	private ThreadPoolExecutor executor = null;
	
	private static TaskManager instance;
	
	private TaskManager() {
		queue = new ArrayBlockingQueue<Runnable>(DEFAULT_QUEUE_SIZE);
		executor = new ThreadPoolExecutor(DEFAULT_CORE_SIZE, DEFAULT_POOL_SIZE, DEFAULT_KEEP_ALIVE, TimeUnit.SECONDS, queue);
		executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());	
	}
	
	synchronized public static TaskManager getInstance() {
		if (instance == null) {
			instance = new TaskManager();
		}
		return instance;
	}
	
	/**
	 * Execute a simple runnable task.
	 * If the queue is full the task is executed in the current Thread.
	 */
	public synchronized void execute(Runnable task) throws InterruptedException {
		if (executor.isTerminating()) throw new InterruptedException();
		log.trace("execute");
		executor.execute(task);
		log.trace("Active Tasks: {} - Pool Size: {}", 
				new Object[]{executor.getActiveCount(), executor.getPoolSize()});
	}
	
	/**
	 * Execute a callable async task.
	 * If the queue is full the task is executed in the current Thread.
	 */
	public synchronized Future submit(Callable task) throws InterruptedException {
		if (executor.isTerminating()) throw new InterruptedException();
		log.trace("submit");
		Future f = executor.submit(task);
		log.trace("Active Tasks: {} - Pool Size: {}", 
				new Object[]{executor.getActiveCount(), executor.getPoolSize()});
		return f;
	}
	
	public void shutdown() {
		executor.shutdown();
		executor.shutdownNow();
	}
	
	@Override
	protected void finalize() throws Throwable {
		this.shutdown();
		super.finalize();
	}

}
